package net.simflow.swf.analyzer.gui;

import java.awt.Toolkit;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;

import net.simflow.swf.analyzer.bgnetwork.opt.BGNetworkBuilder;
import net.simflow.swf.analyzer.bgnetwork.stats.opt.BGNetworkStatsAnalyzer;
import net.simflow.swf.analyzer.data.DataPkg;
import net.simflow.swf.analyzer.data.DirFileList;
import net.simflow.swf.analyzer.data.FilesList;
import net.simflow.swf.analyzer.data.StringConstant;
import net.simflow.swf.analyzer.data.TupleStrInt;
import net.simflow.swf.analyzer.network.data.Flow;
import net.simflow.swf.analyzer.network.data.Network;
import net.simflow.swf.analyzer.network.data.NetworkPkg;
import net.simflow.swf.analyzer.network.opt.FlowBuilder;
import net.simflow.swf.analyzer.network.opt.NetworkMerger;
import net.simflow.swf.analyzer.network.opt.ServiceNetworkMerger;
import net.simflow.swf.analyzer.network.stats.opt.NetworkStatsAnalyzer;
import net.simflow.swf.analyzer.opt.SuccessThread;
import net.simflow.swf.analyzer.t2flow.data.T2FlowDocument;
import net.simflow.swf.analyzer.t2flow.opt.T2FlowFileParser;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.wb.swt.SWTResourceManager;

public class NetworkParserGUI extends Shell {
	public static final String[] EX_FILTER_DIR = { ".t2flow" };
	public static final String[] EX_FILTER_FILE = new String[1];

	private static final int STYLE_NETWORK = 0;
	private static final int STYLE_SERVICE_NETWORK = 1;

	static {
		EX_FILTER_FILE[0] = "";
		for (String f : EX_FILTER_DIR) {
			EX_FILTER_FILE[0] += "*" + f + ";";
		}
	}

	private ScienceWorkflowAnalyzer main;
	private String path;

	NetworkPkg networkPkg = null;
	NetworkPkg sNetworkPkg = null;

	private Button btnParse;
	private Composite cmpFileButton;
	private Group grpInput;
	private Button btnAdd;
	private Button btnRemove;
	private Button btnAddDir;
	private Button btnOpenDir;
	private Button btnClear;
	private Group grpOutput;
	private Composite cmpParseButton;
	private Button btnParseOnlyService;
	private StyledText txtOutput;
	private Table tblFile;

	/**
	 * Create the shell.
	 * 
	 * @param display
	 */
	public NetworkParserGUI(final Display display,
			ScienceWorkflowAnalyzer mainApp) {
		super(display, SWT.CLOSE | SWT.MIN | SWT.TITLE);
		setImage(SWTResourceManager.getImage(NetworkParserGUI.class,
				"/res/icon.png"));

		this.main = mainApp;

		setLayout(new GridLayout(2, false));
		setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

		grpInput = new Group(this, SWT.NONE);
		grpInput.setText("File Operation");
		GridData gd_grpInput = new GridData(SWT.LEFT, SWT.FILL, false, true, 1,
				1);
		gd_grpInput.widthHint = 392;
		grpInput.setLayoutData(gd_grpInput);
		grpInput.setLayout(new GridLayout(1, false));

		cmpFileButton = new Composite(grpInput, SWT.NONE);
		cmpFileButton.setLayout(new FillLayout(SWT.HORIZONTAL));
		cmpFileButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
				false, 1, 1));

		btnAdd = new Button(cmpFileButton, SWT.NONE);
		btnAdd.setText("Add");
		btnAdd.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					FileDialog dialog = new FileDialog(
							display.getActiveShell(), SWT.OPEN);
					try {
						dialog.setFilterPath(path);
					} catch (Exception ee) {
						dialog.setFilterPath(System.getProperty("user.dir"));
					}
					dialog.setFilterExtensions(EX_FILTER_FILE);
					String location = dialog.open();
					if (location == null) {
						return;
					}
					path = location.substring(0, location.lastIndexOf("\\"));

					File file = new File(location);

					HashSet<String> existFiles = new HashSet<String>();
					for (TableItem existFile : tblFile.getItems()) {
						existFiles.add(existFile.getText());
					}

					if (!existFiles.contains(file.getPath())) {
						TableItem item = new TableItem(tblFile, SWT.NONE);
						item.setText(file.getPath());
					}

					tblFile.select(tblFile.getItemCount() - 1);
					tblFile.setTopIndex(tblFile.getSelectionIndex());

				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});

		btnAddDir = new Button(cmpFileButton, SWT.NONE);
		btnAddDir.setText("Add Dir");
		btnAddDir.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					DirectoryDialog dialog = new DirectoryDialog(display
							.getActiveShell());
					if (path == null) {
						path = System.getProperty("user.dir");
					}
					try {
						dialog.setFilterPath(path);
					} catch (Exception ee) {
						dialog.setFilterPath(System.getProperty("user.dir"));
					}
					String location = dialog.open();
					if (location == null) {
						return;
					}
					path = location + "\\";

					File file = new File(location);
					File[] fs = DirFileList.getInstance().list(file,
							EX_FILTER_DIR);

					HashSet<String> existFiles = new HashSet<String>();
					for (TableItem existFile : tblFile.getItems()) {
						existFiles.add(existFile.getText());
					}

					for (File f : fs) {
						if (!existFiles.contains(f.getPath())) {
							TableItem item = new TableItem(tblFile, SWT.NONE);
							item.setText(f.getPath());
						}
					}

					tblFile.select(tblFile.getItemCount() - 1);
					tblFile.setTopIndex(tblFile.getSelectionIndex());

				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});

		btnOpenDir = new Button(cmpFileButton, SWT.NONE);
		btnOpenDir.setText("Open Dir");
		btnOpenDir.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					DirectoryDialog dialog = new DirectoryDialog(display
							.getActiveShell());
					if (path == null) {
						path = System.getProperty("user.dir");
					}
					try {
						dialog.setFilterPath(path);
					} catch (Exception ee) {
						dialog.setFilterPath(System.getProperty("user.dir"));
					}
					String location = dialog.open();
					if (location == null) {
						return;
					}
					path = location + "\\";

					File file = new File(location);
					File[] fs = DirFileList.getInstance().list(file,
							EX_FILTER_DIR);

					tblFile.removeAll();

					for (File f : fs) {

						TableItem item = new TableItem(tblFile, SWT.NONE);
						item.setText(f.getPath());
					}

					tblFile.select(0);
					tblFile.setTopIndex(tblFile.getSelectionIndex());

				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});

		btnRemove = new Button(cmpFileButton, SWT.NONE);
		btnRemove.setText("Remove");
		btnRemove.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (tblFile.getSelectionIndex() < 0
						|| tblFile.getSelectionIndex() >= tblFile
								.getItemCount()) {
					return;
				}
				int slc = tblFile.getSelectionIndex();
				tblFile.remove(slc);
				if (slc >= tblFile.getItemCount()) {
					slc--;
				}
				tblFile.select(slc);
				tblFile.setTopIndex(tblFile.getSelectionIndex());
			}
		});

		btnClear = new Button(cmpFileButton, SWT.NONE);
		btnClear.setText("Clear");
		btnClear.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				tblFile.removeAll();
			}
		});

		tblFile = new Table(grpInput, SWT.BORDER);
		tblFile.setLinesVisible(true);
		tblFile.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

		cmpParseButton = new Composite(grpInput, SWT.NONE);
		cmpParseButton.setLayout(new FillLayout(SWT.HORIZONTAL));
		cmpParseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false,
				false, 1, 1));

		btnParse = new Button(cmpParseButton, SWT.NONE);
		btnParse.setText("Parse");
		btnParse.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				parse(STYLE_NETWORK);
			}
		});

		btnParseOnlyService = new Button(cmpParseButton, SWT.NONE);
		btnParseOnlyService.setText("Parse Only Service");
		btnParseOnlyService.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				parse(STYLE_SERVICE_NETWORK);
			}
		});

		grpOutput = new Group(this, SWT.NONE);
		grpOutput.setText("Output");
		grpOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1,
				1));
		grpOutput.setLayout(new GridLayout(1, false));

		txtOutput = new StyledText(grpOutput, SWT.BORDER | SWT.READ_ONLY
				| SWT.WRAP | SWT.V_SCROLL);
		txtOutput.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
		txtOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1,
				1));
		createContents();
	}

	/**
	 * Create contents of the shell.
	 */
	protected void createContents() {
		setText("File Parser");
		setSize(800, 600);
		setLocation(Toolkit.getDefaultToolkit().getScreenSize().width / 2
				- this.getSize().x / 2, Toolkit.getDefaultToolkit()
				.getScreenSize().height / 2 - this.getSize().y / 2);
	}

	@Override
	protected void checkSubclass() {
		// Disable the check that prevents subclassing of SWT components
	}

	@Override
	public void dispose() {
		super.dispose();
		main.dispose();
	}

	private void parse(final int style) {
		try {
			if (tblFile.getItemCount() <= 0) {
				Printer.getInstance().append(txtOutput,
						"No t2flow Files are selected.",
						StringConstant.WARNING, Printer.COLOR_WARNING);
				return;
			}

			Printer.getInstance().append(txtOutput, "Start parsing...",
					StringConstant.TITLE, SWT.COLOR_BLACK);

			final int[] successCount = { 0 };
			final String[] files = new String[tblFile.getItemCount()];
			for (int i = 0; i < tblFile.getItemCount(); i++) {
				files[i] = tblFile.getItem(i).getText();
			}
			final File[] fs = FilesList.getInstance().list(files);

			final ProcessingMessageBox parsingMessage = new ProcessingMessageBox(
					super.getDisplay().getActiveShell(), "Parsing");
			SuccessThread thread = new SuccessThread(
					"T2Flow Files Parsing Thread") {
				@Override
				public void run() {
					final HashSet<Flow> flowSet = new HashSet<Flow>();
					int count = 0;
					successCount[0] = 0;

					for (File f : fs) {
						if (parsingMessage.isAlive()) {
							parsingMessage.setProgress(count + 1, fs.length);
						}
						count++;

						T2FlowDocument doc = T2FlowFileParser.getInstance()
								.parse(f);
						if (doc == null) {
							Printer.getInstance().append(
									txtOutput,
									"File \"" + f.getPath()
											+ "\" cann't be parsed.",
									StringConstant.ERROR, Printer.COLOR_ERROR);
							continue;
						}

						Flow flow = null;
						switch (style) {
						case STYLE_NETWORK:
							flow = FlowBuilder.getInstance().build(
									doc.getWorkflowBundle());
							break;
						case STYLE_SERVICE_NETWORK:
							flow = ServiceNetworkMerger.getInstance().merge(
									FlowBuilder.getInstance().build(
											doc.getWorkflowBundle()));
							break;
						}

						if (flow == null) {
							Printer.getInstance().append(
									txtOutput,
									"File \"" + f.getPath()
											+ "\" cann't be built to flow.",
									StringConstant.ERROR, Printer.COLOR_ERROR);
							continue;
						} else if (flow.getNodeSet().isEmpty()) {
							Printer.getInstance().append(
									txtOutput,
									"File \"" + f.getPath()
											+ "\" doesn't contain flow.",
									StringConstant.WARNING,
									Printer.COLOR_WARNING);
							continue;
						}

						flowSet.add(flow);
						successCount[0]++;
					}

					Network network;
					Network sNetwork;

					switch (style) {
					case STYLE_NETWORK:
						network = NetworkMerger.getInstance().merge(flowSet);
						networkPkg = packNetwork(Network.NETWORK, network);

						sNetwork = ServiceNetworkMerger.getInstance().merge(
								network);
						sNetworkPkg = packNetwork(Network.SERVICE_NETWORK,
								sNetwork);
						break;
					case STYLE_SERVICE_NETWORK:
						networkPkg = null;
						sNetwork = NetworkMerger.getInstance().merge(flowSet);
						sNetworkPkg = packNetwork(Network.SERVICE_NETWORK,
								sNetwork);
						break;
					}

					synchronized (this) {
						try {
							wait(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}

					Display.getDefault().syncExec(new Runnable() {
						public void run() {
							ArrayList<TupleStrInt> info = new ArrayList<TupleStrInt>();
							info.add(new TupleStrInt("Parsed",
									StringConstant.TITLE));
							info.add(new TupleStrInt(fs.length + " files",
									StringConstant.CONTENT));
							info.add(new TupleStrInt(successCount[0]
									+ " succeeds", StringConstant.CONTENT));
							info.add(new TupleStrInt(
									(fs.length - successCount[0]) + " fails",
									StringConstant.CONTENT));
							Printer.getInstance().append(txtOutput, info,
									SWT.COLOR_BLACK);
						}
					});

					isSuccess = true;
					parsingMessage.close();
				}
			};
			parsingMessage.setThread(thread);
			thread.start();
			if (thread.isAlive()) {
				parsingMessage.open();
			}

			if (!thread.isSuccess()) {
				Printer.getInstance().append(txtOutput, "Parsing is canceled.",
						StringConstant.ERROR, Printer.COLOR_ERROR);
				return;
			}

			synchronized (this) {
				wait(1000);
			}
			DataPkg dataNetworkPkg = new DataPkg();
			DataPkg dataSNetworkPkg = new DataPkg();
			dataNetworkPkg.setNetworkPkg(networkPkg);
			dataSNetworkPkg.setNetworkPkg(sNetworkPkg);
			main.getAnalyzeWholeNetworkGUI().setNetwork(dataNetworkPkg);
			main.getAnalyzeServiceNetworkGUI().setNetwork(dataSNetworkPkg);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}

	private NetworkPkg packNetwork(String title, Network network) {
		NetworkPkg networkPkg;

		network.setName(title);
		networkPkg = new NetworkPkg();
		networkPkg.setNetworkName(network.getName());
		networkPkg.setNetwork(network);
		networkPkg.setNetworkStats(NetworkStatsAnalyzer.getInstance().analyze(
				network));
		networkPkg.setBgNetwork(BGNetworkBuilder.getInstance().build(network));
		networkPkg.setBgFlowMap(BGNetworkBuilder.getInstance().buildBGFlow(
				networkPkg.getBgNetwork()));
		networkPkg.setBgServiceMap(BGNetworkBuilder.getInstance()
				.buildBGService(networkPkg.getBgNetwork()));
		networkPkg.setBgNetworkStats(BGNetworkStatsAnalyzer.getInstance()
				.analyze(networkPkg.getBgNetwork(), networkPkg.getBgFlowMap(),
						networkPkg.getBgServiceMap()));

		return networkPkg;

	}
}
